#include "networkManager.h"





networkManager::networkManager() {

    playerMAX = 0;
    playerCNT = 0;
    runningID = 20;
    isInit = 0;
    debug = 1;

    msgSystem = NULL;
    objManager = NULL;
    dataBase = NULL;
    packet = NULL;
    peer = RakNetworkFactory::GetRakPeerInterface();
}



networkManager::~networkManager() {
    //
}






void networkManager::Init(int pMaxClients, string pHost, int pPort) {
    playerMAX = pMaxClients;

    SocketDescriptor SocketSetup;
    sprintf(SocketSetup.hostAddress, pHost.c_str() );
    SocketSetup.port = pPort;

    peer->Startup(playerMAX, 30, &SocketSetup, 1);
    peer->SetMaximumIncomingConnections(playerMAX);
    isInit = 1;

}



void networkManager::IntroduceMessage(message *pMsgSystem) {
    msgSystem = pMsgSystem;
    if(debug) msgSystem->Add("net: msgSystem linked");
}



void networkManager::IntroduceObjManager(objectManager *pObjManager) {
    objManager = pObjManager;
    if(debug) msgSystem->Add("net: objManager linked");
}

void networkManager::IntroduceSQL(database *pDataBase) {
    dataBase = pDataBase;
    if(debug) msgSystem->Add("net: sql linked");
}




void networkManager::sendMessage(int pType, int pPlayerID) {

    if(isInit) {

        // Struktur
        char Nachricht[512]  = "";
        unsigned char systemType=ID_USER_PACKET_ENUM;
        int tmp=-1;


        //RakNet::BitStream *myBitStream = new RakNet::BitStream;
        RakNet::BitStream myBitStream;
        myBitStream.Write(systemType);
        myBitStream.Write(pType);


        // should ne in case
        StructMsgShowCharacters msgShowCharacters;


        switch(pType) {


        case 100:     // PING
            if(debug) msgSystem->Add("net-send: PING.");
            break;


        case 101:     // PONG
            if(debug) msgSystem->Add("net-send: PONG.");
            break;








        case 0:     // VERSION_ASK:
            if(debug) msgSystem->Add("net-send: VERSION_ASK.");
            break;


        case 2:     // LOGIN_ASK:
            if(debug) msgSystem->Add("net-send: LOGIN_ASK.");
            break;


        case 4:     // LOGIN_ANSWER:
            if(debug) msgSystem->Add("net-send: LOGIN_ASK.");
            if(player[getPlayerIndex(pPlayerID)].authenticated) tmp = 1;
            else tmp = 0;
            myBitStream.Write(tmp);
            break;


        case 7:     // CHARACTER_ASK:
            if(debug) msgSystem->Add("net-send: CHARACTER_ASK.");

            msgShowCharacters = dataBase->getCharacters(player[getPlayerIndex(pPlayerID)].sqlId);
            msgShowCharacters.Write(&myBitStream);

            break;







        case 5:     // CHAT:
            bufferChatRelay.Write(&myBitStream);
            if(debug) msgSystem->Add("net-send: CHAT.");
            break;



        default:
            sprintf(Nachricht,"net-send: type unknown! %i", pType);
            if(debug) msgSystem->Add(Nachricht);
            break;
        }


        // Senden
        if(pPlayerID >= 20) { // single person
            sprintf(Nachricht,"sendMessage: ID: %i", pPlayerID);
            if(debug) msgSystem->Add(Nachricht);

            // exception
            int playerIndex = -1;
            playerIndex = getPlayerIndex(pPlayerID);
            if(playerIndex != -1) peer->Send(&myBitStream,HIGH_PRIORITY, RELIABLE, 0, player[playerIndex].address, false);

        } else { // broadcast
            peer->Send(&myBitStream,HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true);
        }

    }

}

void networkManager::readMessage(Packet *packet, int pPlayerID) {

    if(isInit) {


        // TODO: replace in dynamic single one
        char Nachricht[512]  = "";


        // should be in case
        StructMsgLogin msgLogin;
        StructMsgChat tmpChatMessage;



        // start reading
        RakNet::BitStream myBitStream(packet->data, packet->length, false);


        // check system type
        unsigned char systemType=0;
        myBitStream.Read(systemType); // allways 77=ID_USER_PACKET_ENUM


        // check own message
        int messageType=0;
        myBitStream.Read(messageType);


        switch(messageType) {

        case 100:  // PING
            if(debug) msgSystem->Add("net-got: PING.");
            sendMessage(101,pPlayerID);
            break;

        case 101:  // PONG
            if(debug) msgSystem->Add("net-got: PONG.");
            break;

        case 102:  // mPING (multicast)
            if(debug) msgSystem->Add("net-got: mPING (multicast).");
            break;


        case 104:
            int playerIndex;
            playerIndex = getPlayerIndex(pPlayerID);
            if(playerIndex != -1) player[playerIndex].cntLaag = 0;
            break;


        case 1:     // VERSION_ANSWER
            unsigned short  version;
            myBitStream.Read(version);
            sprintf(Nachricht,"net-got: VERSION_ANSWER (%i).", version);
            if(debug) msgSystem->Add(Nachricht);
            if(version>=1) sendMessage(2,pPlayerID); // LOGIN_ASK
            break;

        case 3:     // LOGIN_ANSWER
            int index, result;
            msgLogin.Read(&myBitStream);
            sprintf(Nachricht,"net-got: LOGIN_ANSWER: (%s/%s)", msgLogin.name.c_str(), msgLogin.pass.c_str() );
            result = -1;
            result = dataBase->checkLogin(msgLogin.name.c_str(), msgLogin.pass.c_str() );
            if( result!= -1 ){

                // save data to player list
                index = getPlayerIndex(pPlayerID);
                player[index].authenticated = 1;
                player[index].sqlId = result; // before send
                player[index].name = msgLogin.name;
                dataBase->InsertLog(3,msgLogin.name);

                // send possible character to client
                sendMessage(7,pPlayerID);

            }
            sendMessage(4,pPlayerID);
            if(debug) msgSystem->Add(Nachricht);

            break;


        case 5:     // CHAT
            tmpChatMessage.Read(&myBitStream);
            bufferChatRelay = tmpChatMessage;
            sendMessage(5,0);
            sprintf(Nachricht,"net-got: CHAT: (%s)", tmpChatMessage.message.c_str());
            if(debug) msgSystem->Add(Nachricht);
            break;


        default:
            sprintf(Nachricht,"net-got: OwnPackage %i NOT handled!", messageType);
            if(debug) msgSystem->Add(Nachricht);
            break;
        }


    } // isinit


}



int networkManager::clientAdd(Packet *packet) {
    if(playerCNT < playerMAX) {

        int playerID = runningID;
        char Nachricht[512]  = "";

        // create temp player
        networkClients tmpPlayer;

        // set up the player stuff
        tmpPlayer.netId = playerID; // overwrite ID
        tmpPlayer.address = packet->systemAddress;
        player.push_back(tmpPlayer);
        playerCNT++;
        runningID++;

        sprintf(Nachricht,"net-Add: new player (%i)", playerID);
        if(debug) msgSystem->Add(Nachricht);

        return playerID;

    } else if(debug) msgSystem->Add("net-Add: max List reached!");

    return -1;
}



void networkManager::clientKill(int pPlayerIndex) {


    if(playerCNT>0) {
        int i;
        bool flag=0;
        for(i=0;i<(playerCNT-1);i++) {
            if(i == pPlayerIndex) flag = 1; // start overwrite
            if(flag) player[i] = player[i+1];
        }
        playerCNT--;
    }

    player.pop_back(); // delete the memory

    char Nachricht[512]  = "";
    sprintf(Nachricht,"clientKill(%i): OK", pPlayerIndex);
    if(debug) msgSystem->Add(Nachricht);

}



int networkManager::getPlayerID(Packet *packet) {

    int i;
    int retVal = -1;
    for(i=0;i<playerCNT;i++) {
        if(player[i].address == packet->systemAddress) retVal = player[i].netId;
    }
    //IMPORTANT: catch exception on each implementation!
    return retVal;
 }



int networkManager::getPlayerIndex(int pPlayerID) {

    int i;
    int retVal = -1;
    for(i=0;i<playerCNT;i++) {
        if(player[i].netId == pPlayerID) retVal = i;
    }
    //IMPORTANT: catch exception on each implementation!
    return retVal;
}




void networkManager::Tick(void) {

    if(isInit) {

        // recive packets
        packet=peer->Receive();
        //cout << "Packet adress: " << packet->systemAddress->ToString() << endl;

        char Nachricht[512]  = "";
        int identifier=0, senderID=0;

        // work until stack is empty
        if(packet!=0) {

			identifier = (int)packet->data[0];
			senderID = getPlayerID(packet); // case 17=-1


            switch(identifier) {


			case 17: // GOT_CONNECTION
			    int tmp;
			    tmp = clientAdd(packet);
				if(tmp!=-1) sendMessage(0, tmp); //VERSION_ASK
			    sprintf(Nachricht,"net: Client connected: %s", packet->systemAddress.ToString());
				if(debug) msgSystem->Add(Nachricht);
				break;


			case 20: // GOT_CONNECTION
			    sprintf(Nachricht,"net: Client lost: %i", senderID);
				if(debug) msgSystem->Add(Nachricht);
				break;


			case ID_USER_PACKET_ENUM:
				//msgSystem->Add("net: OwnPackage income.");
				readMessage(packet, senderID);
				break;

			default:
			    sprintf(Nachricht,"net-listen: player(%i) unhandled: %i.", senderID, packet->data[0]);
				if(debug) msgSystem->Add(Nachricht);
				break;

			}

			peer->DeallocatePacket(packet);

		}

    }
}



void networkManager::TickSecond(void) {

    int i, terminate = -1;
    for(i=0;i<playerCNT;i++) {
        player[i].cntLaag++;
        if(player[i].cntLaag > 10) terminate = i;
    }

    // use temp var, each round one, otherwise he change the index while runf in an loop
    if(terminate!=-1) clientKill(terminate);

}


void networkManager::Draw(int row) {

    attrset(A_UNDERLINE | A_BOLD | COLOR_PAIR(1));
    attrset(A_NORMAL | COLOR_PAIR(2));
    mvprintw(row-2,0,"Player");
    mvprintw(row-1,1,"netId");
    mvprintw(row-1,7,"sqlId");
    mvprintw(row-1,13,"chrId");
    mvprintw(row-1,19,"Adress");
    mvprintw(row-1,42,"Index");
    mvprintw(row-1,49,"Laag");
    //mvprintw(row-1,40,"sqlId");

    int i;
    for(i=0;i<playerCNT;i++) {
        mvprintw(row+i,1,"%i", player[i].netId );
        mvprintw(row+i,7,"%i", player[i].sqlId );
        mvprintw(row+i,13,"%i", player[i].chrId );
        mvprintw(row+i,19,"%s", player[i].address.ToString() );
        mvprintw(row+i,42,"%i", i );
        mvprintw(row+i,49,"%i", player[i].cntLaag );
    }
}

int networkManager::getConnections(void) {
    return playerCNT;
}


